from enum import Enum

class TokenValueType(Enum):
    UNKNOWN = 0
    OPERATOR = 1
    NUMBER = 2
    PATTERN = 3
    WHITESPACE = 4
    LETTER = 5
    LPATTERN = 6
    RPATTERN = 7
    TERMINAL = 8
    INT = 9
    FLOAT = 10
    SYMBOL = 11

class Token:
    def __init__(self, type: TokenValueType, value: str) -> None:
        self.type = type
        self.value = value
        self.token_dict = {"type":self.type.name,"value":self.value}
    
    def __str__(self) -> str:
        return 'Token ' + str(self.token_dict)+'\n'

class TokenSwitcher:
    def switch_operator(chr):
        return chr in ["+", "-", "*", "/", "=", "!"]

    def switch_number(chr):
        return chr in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    
    def switch_pattern(chr):
        return chr in ['(', ')']
    
    def switch_letter(chr):
        return chr in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                       'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    
    def switch_whitespace(chr):
        return chr in [' ', '\r', '\n', '\t']
    
    def switch_terminal(chr):
        return chr == ";"
        
    def switch_dot(chr):
        return chr == "."

    def switch(chr):
        if TokenSwitcher.switch_number(chr):
            return Token(TokenValueType.NUMBER, chr)
        elif TokenSwitcher.switch_operator(chr):
            return Token(TokenValueType.OPERATOR, chr)
        elif TokenSwitcher.switch_pattern(chr):
            return Token(TokenValueType.PATTERN, chr)
        elif TokenSwitcher.switch_letter(chr):
            return Token(TokenValueType.LETTER, chr)
        elif TokenSwitcher.switch_whitespace(chr):
            return Token(TokenValueType.WHITESPACE, chr)
        elif TokenSwitcher.switch_terminal(chr):
            return Token(TokenValueType.TERMINAL, chr)
        else:
            return Token(TokenValueType.UNKNOWN, chr)
    
    def lex_token(src: str):
        tokens = []
        seek = 0
        src_size = len(src)
        while seek < src_size:
            tokens.append(TokenSwitcher.switch(src[seek]))
            seek+=1
        
        return tokens

def forward_read(seek, tokens, token_type: TokenValueType):
    value = tokens[seek].value
    while seek+1 < len(tokens) and tokens[seek+1].type == token_type:
        value += tokens[seek+1].value
        seek+=1
    return seek, Token(token_type, value)

def lex(tokens: list):
    result = [] 
    seek = 0
    tokens_size = len(tokens)
    while seek < tokens_size:
        if tokens[seek].type == TokenValueType.NUMBER:
            seek, token = forward_read(seek, tokens, TokenValueType.NUMBER)
            result.append(token)
        elif tokens[seek].type == TokenValueType.OPERATOR:
            seek, token = forward_read(seek, tokens, TokenValueType.OPERATOR)
            result.append(token)
        elif tokens[seek].type == TokenValueType.PATTERN:
            if tokens[seek].value == '(':result.append(Token(TokenValueType.LPATTERN, '('))
            elif tokens[seek].value == ')':result.append(Token(TokenValueType.RPATTERN, ')'))
        elif tokens[seek].type == TokenValueType.TERMINAL:
            result.append(Token(TokenValueType.TERMINAL, ';'))
        elif tokens[seek].type == TokenValueType.LETTER:
            seek, token = forward_read(seek, tokens, TokenValueType.LETTER)
            result.append(token)
        elif tokens[seek].type == TokenValueType.WHITESPACE:
            seek, token = forward_read(seek, tokens, TokenValueType.WHITESPACE)
            #result.append(token)
        else:
            seek, token = forward_read(seek, tokens, TokenValueType.UNKNOWN)
            result.append(token)
        seek+=1
    return result

class Lex:
    def __init__(self) -> None:
        pass

    def switch_operator(self, chr):
        return chr in ["+", "-", "*", "/", "=", "!", "."]

    def switch_number(self, chr):
        return chr in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    
    def switch_pattern(self, chr):
        return chr in ['(', ')']
    
    def switch_letter(self, chr):
        return chr in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                       'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
    
    def switch_whitespace(self, chr):
        return chr in [' ', '\r', '\n', '\t']
    
    def switch_terminal(self, chr):
        return chr == ";"

    def switch(self, chr: str):
        if self.switch_number(chr):
            return Token(TokenValueType.NUMBER, chr)
        elif self.switch_operator(chr):
            return Token(TokenValueType.OPERATOR, chr)
        elif self.switch_pattern(chr):
            return Token(TokenValueType.PATTERN, chr)
        elif self.switch_letter(chr):
            return Token(TokenValueType.LETTER, chr)
        elif self.switch_whitespace(chr):
            return Token(TokenValueType.WHITESPACE, chr)
        elif self.switch_terminal(chr):
            return Token(TokenValueType.TERMINAL, chr)
        else:
            return Token(TokenValueType.UNKNOWN, chr)
    
    def forward_read(self, seek, src):
        root_token = self.switch(src[seek])
        value, token_type = root_token.value, root_token.type
        while seek+1 < len(src) and self.switch(src[seek+1]).type == token_type:
            value += self.switch(src[seek+1]).value
            seek+=1
        return seek, Token(token_type, value)

    def lex(self, src:str):
        result = []

        seek = 0
        src_size = len(src)
        while seek < src_size:
            token = self.switch(src[seek])
            if token.type == TokenValueType.NUMBER:
                value = token.value
                isint = True
                while seek+1 < src_size:
                    if self.switch(src[seek+1]).type == token.type:pass
                    elif self.switch(src[seek+1]).value == ".":isint = False
                    else:break
                    value += self.switch(src[seek+1]).value
                    seek+=1
                if isint:
                    result.append(Token(TokenValueType.INT, value))
                else:
                    result.append(Token(TokenValueType.FLOAT, value))
            elif token.type == TokenValueType.OPERATOR:
                seek, token = self.forward_read(seek, src)
                result.append(token)
            elif token.type == TokenValueType.PATTERN:
                if token.value == '(':result.append(Token(TokenValueType.LPATTERN, '('))
                elif token.value == ')':result.append(Token(TokenValueType.RPATTERN, ')'))
            elif token.type == TokenValueType.TERMINAL:
                result.append(Token(TokenValueType.TERMINAL, ';'))
            elif token.type == TokenValueType.LETTER:
                seek, token = self.forward_read(seek, src)
                result.append(token)
            elif token.type == TokenValueType.WHITESPACE:
                seek, token = self.forward_read(seek, src)
                #result.append(token)
            else:
                seek, token = self.forward_read(seek, src)
                result.append(token)
            seek+=1

        return result